﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using Argos.Activities.MessageDelivery;
using Argos.Activities.Tests;
using Argos.Workflows;
using System.Workflow.Runtime.Hosting;
using System.Xml;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Workflow.Runtime;

namespace Argos
{
	internal static class ExtensionMethods
	{

		//-------------------------------------------------------------------
		// System.Type

		#region public static object CreateNewInstanceFrom(this Type t, string s)

		/// <summary>
		/// Creates a new instance of the current type and parses the given
		/// string to the current type.
		/// </summary>
		/// <param name="t">The current type.</param>
		/// <param name="s">The string to parse into a new instance of the current type.</param>
		public static object CreateNewInstanceFrom(this Type t, string s)
		{
			if (t.IsEnum)
			{
				return Enum.Parse(t, s);
			}

			MethodInfo mi = t.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
			if (null != mi)
			{
				return mi.Invoke(null, new object[] { s });
			}

			if (t.ImplementsInterface(typeof(IConvertible)))
			{
				return Convert.ChangeType(s, t);
			}

			ConstructorInfo ci = null;
			ConstructorInfo[] cis = t.GetConstructors();

			foreach (var item in cis)
			{
				ParameterInfo[] pis = item.GetParameters();
				if (null != pis && pis.Length == 1 && pis[0].ParameterType == typeof(string))
				{
					ci = item;
					break;
				}
			}

			if (null != ci)
			{
				return ci.Invoke(new object[] { s });
			}

			throw new InvalidCastException(string.Format("Unable to parse the given string to the type '{0}'.", t));
		}

		#endregion
		
		#region public static string GetDescription(this Type t)

		/// <summary>
		/// Returns the description for the current type.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The description is read from the <see cref="DescriptionAttribute"/> attribute.
		/// </para>
		/// <para>
		/// If the current type is not decorated with that attribute, the method returns null.
		/// </para>
		/// </remarks>
		/// <param name="t">The current type.</param>
		public static string GetDescription(this Type t)
		{
			return t.GetDescription(null);
		}

		#endregion

		#region public static string GetDescription(this Type t, string defaultDescription)

		/// <summary>
		/// Returns the description for the current type.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The description is read from the <see cref="DescriptionAttribute"/> attribute.
		/// </para>
		/// <para>
		/// If the current type is not decorated with that attribute, the method returns the
		/// value specified in <paramref name="defaultDescription"/>.
		/// </para>
		/// </remarks>
		/// <param name="t">The current type.</param>
		/// <param name="defaultDescription">
		/// The default description to return if the current type is not decorated
		/// with the <see cref="DescriptionAttribute"/> attribute.
		/// </param>
		public static string GetDescription(this Type t, string defaultDescription)
		{
			object[] attribs = t.GetCustomAttributes(typeof(DescriptionAttribute), false);
			if (null != attribs && attribs.Length > 0)
			{
				DescriptionAttribute a = (DescriptionAttribute)attribs[0];
				return !string.IsNullOrEmpty(a.Description) ? a.Description : null;
			}
			return defaultDescription;
		}

		#endregion
		
		#region public static string GetDisplayName(this Type t)

		/// <summary>
		/// Returns the display name for the current type.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The display name is read from the <see cref="DisplayNameAttribute"/> attribute.
		/// </para>
		/// <para>
		/// If the current type is not decorated with that attribute, the method returns null.
		/// </para>
		/// </remarks>
		/// <param name="t">The current type.</param>
		public static string GetDisplayName(this Type t)
		{
			return t.GetDisplayName(null);
		}

		#endregion

		#region public static string GetDisplayName(this Type t, string defaultDisplayName)

		/// <summary>
		/// Returns the display name for the current type.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The display name is read from the <see cref="DisplayNameAttribute"/> attribute.
		/// </para>
		/// <para>
		/// If the current type is not decorated with that attribute, the method returns
		/// the value specified in <paramref name="defaultDisplayName"/>.
		/// </para>
		/// </remarks>
		/// <param name="t">The current type.</param>
		/// <param name="defaultDisplayName">
		/// The display name to return if the current type is not decorated with
		/// the <see cref="DisplayNameAttribute"/> attribute.
		/// </param>
		public static string GetDisplayName(this Type t, string defaultDisplayName)
		{
			object[] attribs = t.GetCustomAttributes(typeof(DisplayNameAttribute), false);
			if (null != attribs && attribs.Length > 0)
			{
				DisplayNameAttribute a = (DisplayNameAttribute)attribs[0];
				return !string.IsNullOrEmpty(a.DisplayName) ? a.DisplayName : null;
			}
			return defaultDisplayName;
		}

		#endregion

		#region public static MethodInfo GetStaticParser(this Type t)

		/// <summary>
		/// Returns the static method <c>Parse</c> for the current type that takes
		/// one string as a parameter.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static MethodInfo GetStaticParser(this Type t)
		{
			return t.GetMethod("Parse", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
		}

		#endregion

		#region public static bool HasConstructor(this Type t, params Type[] parameterTypes)

		/// <summary>
		/// Returns whether the current type has a constructor that takes parameters
		/// that match the given types.
		/// </summary>
		/// <param name="t">The current type.</param>
		/// <param name="parameterTypes">An array of types for the constructor's parameters.</param>
		public static bool HasConstructor(this Type t, params Type[] parameterTypes)
		{
			foreach (var constructor in t.GetConstructors())
			{
				ParameterInfo[] parameters = constructor.GetParameters();
				if (parameters.Length == parameterTypes.Length)
				{
					bool match = true;
					for (int i = 0; i < parameters.Length; i++)
					{
						if (!object.Equals(parameters[i].ParameterType, parameterTypes[i]))
						{
							match = false;
							break;
						}
					}
					if (match)
						return true;
				}
			}

			return false;
		}

		#endregion
		
		#region public static bool HasPublicDefaultConstructor(this Type t)

		/// <summary>
		/// Returns whether the current <c>Type</c> defines a public
		/// default (parameterless) constructor.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool HasPublicDefaultConstructor(this Type t)
		{
			ConstructorInfo ci = t.GetConstructor(new Type[0]);
			if (null == ci)
			{
				return false;
			}

			if (!ci.IsPublic)
			{
				return false;
			}

			return true;
		}

		#endregion
		
		#region public static bool ImplementsInterface(this Type t, Type interfaceType)

		/// <summary>
		/// Returns whether the current <c>Type</c> implements the interface
		/// given in <paramref name="interfaceType"/>.
		/// </summary>
		/// <param name="t">The current type.</param>
		/// <param name="interfaceType">The interface type to check.</param>
		public static bool ImplementsInterface(this Type t, Type interfaceType)
		{
			if (null == interfaceType)
			{
				throw new ArgumentNullException("interfaceType");
			}

			if (!interfaceType.IsInterface)
			{
				throw new ArgumentException("The type is not an interface type.", "interfaceType");
			}

			Predicate<Type> interfaceFinder = delegate(Type t2)
			{
				return t2.Equals(interfaceType);
			};

			List<Type> interfaces = new List<Type>(t.GetInterfaces());
			if (null == interfaces.Find(interfaceFinder))
			{
				return false;
			}

			return true;
		}

		#endregion

		#region public static bool IsBrowsable(this Type t)

		/// <summary>
		/// Returns whether the current type is browsable.
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		/// <remarks>
		/// A browsable type is a type that has not been marked with the <see cref="BrowsableAttribute"/>
		/// attribute, or if the <see cref="BrowsableAttribute.Browsable"/> property on that attribute
		/// has been set to true.
		/// </remarks>
		public static bool IsBrowsable(this Type t)
		{
			if (null == t)
			{
				throw new ArgumentNullException("t");
			}

			object[] attribs = t.GetCustomAttributes(typeof(BrowsableAttribute), false);
			if (null != attribs && attribs.Length > 0)
			{
				BrowsableAttribute a = (BrowsableAttribute)attribs[0];
				return a.Browsable;
			}

			return true;
		}

		#endregion

		#region public static bool IsMessageDeliveryActivity(this Type t)

		/// <summary>
		/// Returns whether the current <c>Type</c> is a message delivery activity.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsMessageDeliveryActivity(this Type t)
		{
			if (t.IsAbstract)
			{
				return false;
			}
			if (!t.IsSubclassOf(typeof(Activity)))
			{
				return false;
			}
			if (!t.HasPublicDefaultConstructor())
			{
				return false;
			}
			if (!t.IsBrowsable())
			{
				return false;
			}
			return t.ImplementsInterface(typeof(IMessageDeliveryActivity));
		}

		#endregion

		#region public static bool IsParseable(this Type t)

		/// <summary>
		/// Returns whether the current type is a parseable type. A parseable type
		/// defines a static method <c>Parse</c> that takes a single parameter of
		/// type <see cref="string"/>.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsParseable(this Type t)
		{
			return null != t.GetStaticParser();
		}

		#endregion
		
		#region public static bool IsStateMachineWorkflow(this Type t)

		/// <summary>
		/// Returns whether the current type is a state machine workflow.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsStateMachineWorkflow(this Type t)
		{
			return t == typeof(StateMachineWorkflowActivity) || t.IsSubclassOf(typeof(StateMachineWorkflowActivity));
		}

		#endregion

		#region public static bool IsTargetMonitoringWorkflow(this Type t)

		/// <summary>
		/// Returns whether the current <c>Type</c> is a target monitoring workflow type.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsTargetMonitoringWorkflow(this Type t)
		{
			if (t.IsAbstract)
			{
				return false;
			}
			if (!t.IsSubclassOf(typeof(StateMachineWorkflowActivity)) && !t.IsSubclassOf(typeof(SequentialWorkflowActivity)))
			{
				return false;
			}
			if (!t.IsBrowsable())
			{
				return false;
			}
			return t.ImplementsInterface(typeof(ITargetMonitoringWorkflow));
		}

		#endregion

		#region public static bool IsTestActivity(this Type t)

		/// <summary>
		/// Returns whether the current <c>Type</c> is a test activity type.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsTestActivity(this Type t)
		{
			if (t.IsAbstract)
			{
				return false;
			}
			if (!t.IsSubclassOf(typeof(Activity)))
			{
				return false;
			}
			if (!t.HasPublicDefaultConstructor())
			{
				return false;
			}
			if (!t.IsBrowsable())
			{
				return false;
			}
			return t.ImplementsInterface(typeof(ITestActivity));
		}

		#endregion

		#region public static bool IsWorkflowService(this Type t)

		/// <summary>
		/// Returns whether the given type is a workflow service type.
		/// </summary>
		/// <param name="t">The current type.</param>
		public static bool IsWorkflowService(this Type t)
		{
			if (t.IsSubclassOf(typeof(WorkflowRuntimeService)) || t == typeof(WorkflowRuntimeService))
			{
				return true;
			}

			object[] arr = t.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), true);
			if (null != arr && arr.Length > 0)
			{
				return true;
			}

			return false;
		}

		#endregion

		//-------------------------------------------------------------------

		
		//-------------------------------------------------------------------
		// System.Uri

		#region public static bool IsHttp(this Uri u)

		/// <summary>
		/// Returns whether the current <c>Uri</c> is a HTTP URI, either <c>http</c> or <c>https</c>.
		/// </summary>
		/// <param name="u">The current <c>Uri</c>.</param>
		public static bool IsHttp(this Uri u)
		{
			return string.Equals(u.Scheme, "http", StringComparison.OrdinalIgnoreCase) || string.Equals(u.Scheme, "https", StringComparison.OrdinalIgnoreCase);
		}

		#endregion

		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Reflection.PropertyInfo

		#region public static bool IsBrowsable(this PropertyInfo property)

		/// <summary>
		/// Returns whether the current property is browsable.
		/// </summary>
		/// <param name="property">The current property.</param>
		public static bool IsBrowsable(this PropertyInfo property)
		{
			if (null == property)
			{
				throw new ArgumentNullException("property");
			}

			object[] attribs = property.GetCustomAttributes(typeof(BrowsableAttribute), false);
			if (null != attribs && attribs.Length > 0)
			{
				BrowsableAttribute a = (BrowsableAttribute)attribs[0];
				return a.Browsable;
			}

			return true;
		}

		#endregion

		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.String

		#region public static string Left(this string s, int maxLength)

		/// <summary>
		/// Returns the maximum of <paramref name="maxLength"/> characters from the beginning of <paramref name="s"/>.
		/// </summary>
		/// <param name="maxLength">The maximum number of characters in the resulting string.</param>
		public static string Left(this string s, int maxLength)
		{
			if (maxLength > 0 && s.Length > maxLength)
			{
				s = s.Substring(0, maxLength);
			}
			return s;
		}

		#endregion

		#region public static Type ToType(this string s)

		/// <summary>
		/// Parses the current string to a <see cref="Type"/> type.
		/// </summary>
		/// <param name="s">The current string.</param>
		public static Type ToType(this string s)
		{
			Type t = null;

			if (!string.IsNullOrEmpty(s))
			{
				try
				{
					t = Type.GetType(s);
				}
				catch
				{
					if (s.Contains(","))
					{
						t = Type.GetType(s.Substring(0, s.IndexOf(',')));
					}
				}
			}

			return t;
		}

		#endregion
		
		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Xml.XmlReader

		#region public static Type ReadAttributeContentAsType(this XmlReader reader)

		/// <summary>
		/// Reads the value from the current attribute and converts it to a Type instance.
		/// </summary>
		/// <param name="reader">The current reader.</param>
		public static Type ReadAttributeContentAsType(this XmlReader reader)
		{
			return reader.ReadContentAsString().ToType();
		}

		#endregion

		#region public static void ReadElementContent(this XmlReader reader, IDictionary dictionary, string itemElementName, string keyElementName, string valueElementName)

		/// <summary>
		/// Reads the contents of the current element to the given dictionary.
		/// </summary>
		/// <param name="reader">The current reader.</param>
		/// <param name="dictionary">The dictionary to populate.</param>
		/// <param name="itemElementName">The name of the element representing each item in the dictionary.</param>
		/// <param name="keyElementName">The name of the element representing the key of each item in the dictionary.</param>
		/// <param name="valueElementName">The name of the element representing the value of each item in the dictionary.</param>
		public static void ReadElementContent(this XmlReader reader, IDictionary dictionary, string itemElementName, string keyElementName, string valueElementName)
		{

			if (null == dictionary)
				throw new ArgumentNullException("dictionary");

			if (string.IsNullOrEmpty(itemElementName))
				throw new ArgumentNullException("itemElementName");

			if (string.IsNullOrEmpty(keyElementName))
				throw new ArgumentNullException("keyElementName");

			if (string.IsNullOrEmpty(valueElementName))
				throw new ArgumentNullException("valueElementName");


			reader.ReadStartElement();

			while (reader.NodeType == XmlNodeType.Element)
			{
				if (reader.LocalName == itemElementName)
				{

					#region Item element

					// Start item element
					reader.ReadStartElement(itemElementName);


					#region Key element

					if (reader.LocalName != keyElementName)
						throw new InvalidOperationException(string.Format("Unrecognized key element: '{0}'.", reader.LocalName));

					object key = reader.ReadObjectElement();

					#endregion

					#region Value element

					object value = null;
					if (reader.LocalName == valueElementName)
					{
						value = reader.ReadObjectElement();
					}

					#endregion

					dictionary.Add(key, value);

					reader.ReadEndElement();
					// End item element.

					#endregion

				}
			}

			reader.ReadEndElement();
		}

		#endregion

		#region public static void ReadElementContent(this XmlReader reader, IList list, string itemElementName)

		/// <summary>
		/// Reads the contents of the current element to the given list.
		/// </summary>
		/// <param name="reader">The current reader.</param>
		/// <param name="list">The list to populate.</param>
		/// <param name="itemElementName">The name of the element representing each item in the list.</param>
		public static void ReadElementContent(this XmlReader reader, IList list, string itemElementName)
		{

			if (null == list)
				throw new ArgumentNullException("list");

			if (string.IsNullOrEmpty(itemElementName))
				throw new ArgumentNullException("itemElementName");

			reader.ReadStartElement();


			while (reader.NodeType == XmlNodeType.Element)
			{
				if (reader.LocalName != itemElementName)
					throw new InvalidOperationException(string.Format("Unrecognized element: '{0}'.", reader.LocalName));

				object o = reader.ReadObjectElement();
				if (null != o)
					list.Add(o);

			}

			reader.ReadEndElement();

		}

		#endregion

		#region public static DateTime ReadExactDateTimeElement(this XmlReader reader)

		/// <summary>
		/// Reads the contents of the current element and returns it as a <see cref="DateTime"/>
		/// object.
		/// </summary>
		/// <param name="reader">The current reader.</param>
		public static DateTime ReadExactDateTimeElement(this XmlReader reader)
		{
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(reader.ReadOuterXml());

			long l = 0;
			if (long.TryParse(doc.DocumentElement.GetAttribute("ticks"), out l))
			{
				return new DateTime(l, DateTimeKind.Utc);
			}

			throw new InvalidOperationException("Cannot read the current element into a DateTime object.");
		}

		#endregion
		
		#region public static object ReadObjectElement(this XmlReader reader)

		/// <summary>
		/// Reads the object from the current element and returns it.
		/// </summary>
		/// <param name="reader">The current reader.</param>
		public static object ReadObjectElement(this XmlReader reader)
		{
			if (reader.NodeType != XmlNodeType.Element)
				throw new InvalidOperationException(string.Format("The node type '{0}' is invalid for this operation.", reader.NodeType));

			XmlDocument doc = new XmlDocument();
			doc.LoadXml(reader.ReadOuterXml());

			Type t = doc.DocumentElement.GetAttribute("type").ToType();
			bool isBinary = doc.DocumentElement.GetAttribute("binary") == "true";
			string s = doc.DocumentElement.InnerText;
			object o = null;

			if (isBinary)
			{
				byte[] buffer = Convert.FromBase64String(s);
				BinaryFormatter bf = new BinaryFormatter();
				using (MemoryStream strm = new MemoryStream())
				{
					strm.Write(buffer, 0, buffer.Length);
					strm.Position = 0;
					o = bf.Deserialize(strm);
				}
			}
			else if (t == typeof(string))
			{
				o = s;
			}
			else if (t.IsEnum)
			{
				o = Enum.Parse(t, s);
			}
			else if (t == typeof(DateTime))
			{
				o = DateTime.Parse(s, null, System.Globalization.DateTimeStyles.RoundtripKind);
			}
			else if (t == typeof(Type) || t.FullName == "System.RuntimeType")
			{
				o = s.ToType();
			}
			else if (t.IsParseable())
			{
				MethodInfo mi = t.GetStaticParser();
				o = mi.Invoke(null, new object[] { s });
			}
			else if (t.HasConstructor(typeof(string)))
			{
				o = Activator.CreateInstance(t, s);
			}

			return o;
		}

		#endregion
		
		#region public static Type ReadElementContentAsType(this XmlReader reader)

		/// <summary>
		/// Reads the string from the current element and converts it to
		/// a <see cref="Type"/> instance.
		/// </summary>
		/// <param name="reader">The reader to read from.</param>
		public static Type ReadElementContentAsType(this XmlReader reader)
		{
			return reader.ReadElementContentAsString().ToType();
		}

		#endregion

		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Xml.XmlWriter

		#region public static void WriteExactDateTimeElement(this XmlWriter writer, DateTime dateTime, string elementName)

		/// <summary>
		/// Writes an element to the current writer containing the given date and time as an exact
		/// value so that it can be parsed back to exactly an identical date and time.
		/// </summary>
		/// <remarks>
		/// The time is always written in universal time in order to be exact.
		/// </remarks>
		/// <param name="writer">The current writer.</param>
		/// <param name="dateTime">The date and time to write.</param>
		/// <param name="elementName">The name of the element representing the date and time.</param>
		public static void WriteExactDateTimeElement(this XmlWriter writer, DateTime dateTime, string elementName)
		{
			if (string.IsNullOrEmpty(elementName))
				throw new ArgumentNullException("elementName");

			XmlDocument doc = new XmlDocument();
			doc.LoadXml(string.Format("<{0}/>", elementName));

			DateTime dt = dateTime.ToUniversalTime();

			doc.DocumentElement.InnerText = dt.ToString("u");
			doc.DocumentElement.SetAttribute("ticks", dt.Ticks.ToString());

			writer.WriteRaw(doc.DocumentElement.OuterXml);
		}

		#endregion
		
		#region public static void WriteDictionaryElement(this XmlWriter writer, IDictionary dictionary, string dictionaryElementName, string itemElementName, string keyElementName, string valueElementName)

		/// <summary>
		/// Writes the given dictionary to the given XML writer instance.
		/// </summary>
		/// <param name="writer">The current writer.</param>
		/// <param name="dictionary">The dictionary to write.</param>
		/// <param name="dictionaryElementName">The name of the element representing the dictionary.</param>
		/// <param name="itemElementName">The name of the element representing each item in the dictionary.</param>
		/// <param name="keyElementName">The name of the element representing the key of each item in the dictionary.</param>
		/// <param name="valueElementName">The name of the element representing the value of each item in the dictionary.</param>
		/// <exception cref="ArgumentNullException">The exception that is thrown if any of the parameters are null.</exception>
		public static void WriteDictionaryElement(this XmlWriter writer, IDictionary dictionary, string dictionaryElementName, string itemElementName, string keyElementName, string valueElementName)
		{
			if (null == dictionary)
				throw new ArgumentNullException("dictionary");

			if (dictionary.Count == 0)
				return;

			if (string.IsNullOrEmpty(dictionaryElementName))
				throw new ArgumentNullException("dictionaryElementName");

			if (string.IsNullOrEmpty(itemElementName))
				throw new ArgumentNullException("itemElementName");

			if (string.IsNullOrEmpty(keyElementName))
				throw new ArgumentNullException("keyElementName");

			if (string.IsNullOrEmpty(valueElementName))
				throw new ArgumentNullException("valueElementName");


			// Start dictionary element.
			writer.WriteStartElement(dictionaryElementName);

			lock (dictionary.SyncRoot)
			{
				foreach (object key in dictionary.Keys)
				{
					// Start item element.
					writer.WriteStartElement(itemElementName);

					// Start key element
					writer.WriteObjectElement(keyElementName, key);
					// End key element

					object value = dictionary[key];
					if (null != value)
					{
						// Start value element.
						writer.WriteObjectElement(valueElementName, value);
						// End value element.
					}

					writer.WriteEndElement();
					// End item element.
				}
			}

			writer.WriteEndElement();
			// End dictionary element.
		}

		#endregion

		#region public static void WriteListElement(this XmlWriter writer, IList list, string listElementName, string itemElementName)

		/// <summary>
		/// Writes the given list to the given XML writer instance.
		/// </summary>
		/// <param name="writer">The current writer.</param>
		/// <param name="list">The list to write.</param>
		/// <param name="listElementName">The name of the element representing the list.</param>
		/// <param name="itemElementName">The name of the element representing each item in the list.</param>
		/// <exception cref="ArgumentNullException">The exception that is thrown if any of the parameters are null.</exception>
		public static void WriteListElement(this XmlWriter writer, IList list, string listElementName, string itemElementName)
		{
			if (null == list)
				throw new ArgumentNullException("list");

			if (string.IsNullOrEmpty(listElementName))
				throw new ArgumentNullException("listElementName");

			if (string.IsNullOrEmpty(itemElementName))
				throw new ArgumentNullException("itemElementName");


			writer.WriteStartElement(listElementName);

			lock (list.SyncRoot)
			{
				foreach (object item in list)
				{
					if (null != item)
					{
						writer.WriteObjectElement(itemElementName, item);
					}
				}
			}

			writer.WriteEndElement();

		}

		#endregion
		
		#region public static void WriteObjectElement(this XmlWriter writer, string elementName, object obj)

		/// <summary>
		/// Writes a new element containing the given object.
		/// </summary>
		/// <param name="writer">The current writer.</param>
		/// <param name="elementName">The name of the element to write.</param>
		/// <param name="obj">The object to write.</param>
		public static void WriteObjectElement(this XmlWriter writer, string elementName, object obj)
		{
			if (null == obj)
				return;

			if (string.IsNullOrEmpty(elementName))
				throw new ArgumentNullException("elementName");

			XmlDocument doc = new XmlDocument();
			doc.LoadXml(string.Format("<{0}/>", elementName));

			doc.DocumentElement.SetAttribute("type", string.Format("{0}, {1}", obj.GetType(), obj.GetType().Assembly));

			if (obj is string)
			{
				doc.DocumentElement.InnerText = (string)obj;
			}
			else if (obj is Uri)
			{
				doc.DocumentElement.InnerText = obj.ToString();
			}
			else if (obj is DateTime)
			{
				doc.DocumentElement.InnerText = ((DateTime)obj).ToString("u");
			}
			else if (obj is Guid)
			{
				doc.DocumentElement.InnerText = obj.ToString();
			}
			else if (obj is Type)
			{
				doc.DocumentElement.InnerText = string.Format("{0}, {1}", obj, ((Type)obj).Assembly);
			}
			else if (obj.GetType().IsEnum)
			{
				doc.DocumentElement.InnerText = obj.ToString();
			}
			else if (obj.GetType().IsParseable())
			{
				if (obj.GetType().ImplementsInterface(typeof(IFormattable)))
				{
					doc.DocumentElement.InnerText = ((IFormattable)obj).ToString(null, null);
				}
				else
				{
					doc.DocumentElement.InnerText = obj.ToString();
				}
			}
			else if (obj.GetType().IsSerializable)
			{
				BinaryFormatter bf = new BinaryFormatter();
				byte[] buffer = null;
				using (MemoryStream strm = new MemoryStream())
				{
					bf.Serialize(strm, obj);
					strm.Position = 0;
					buffer = new byte[strm.Length];
					strm.Read(buffer, 0, buffer.Length);
				}
				doc.DocumentElement.InnerText = Convert.ToBase64String(buffer, Base64FormattingOptions.InsertLineBreaks);
				doc.DocumentElement.SetAttribute("binary", "true");
			}
			else if (obj is IConvertible)
			{
				doc.DocumentElement.InnerText = Convert.ToString(obj);
			}
			else
			{
				doc.DocumentElement.InnerText = obj.ToString();
			}

			writer.WriteRaw(doc.DocumentElement.OuterXml);
		}

		#endregion
		
		#region public static void WriteTypeAttribute(this XmlWriter writer, Type type, string attributeName)

		/// <summary>
		/// Writes the given type to the current writer as an attribute.
		/// </summary>
		/// <param name="writer">The current writer.</param>
		/// <param name="type">The type to write.</param>
		/// <param name="attributeName">The name of the attribute.</param>
		public static void WriteTypeAttribute(this XmlWriter writer, Type type, string attributeName)
		{
			if (null == type)
				throw new ArgumentNullException("type");

			if (string.IsNullOrEmpty(attributeName))
				throw new ArgumentNullException("attributeName");

			writer.WriteAttributeString(attributeName, string.Format("{0}, {1}", type, type.Assembly));
		}

		#endregion

		#region public static void WriteTypeElement(this XmlWriter writer, Type type, string elementName)

		/// <summary>
		/// Writes an element to the current writer with the string representation
		/// of the given type.
		/// </summary>
		/// <param name="writer">The current writer.</param>
		/// <param name="type">The type to write.</param>
		/// <param name="elementName">The name of the element containing the written type.</param>
		public static void WriteTypeElement(this XmlWriter writer, Type type, string elementName)
		{
			if (null == type)
				throw new ArgumentNullException("type");

			if (string.IsNullOrEmpty(elementName))
				throw new ArgumentNullException("elementName");

			writer.WriteElementString(elementName, string.Format("{0}, {1}", type, type.Assembly));
		}

		#endregion

		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Workflow.Runtime.WorkflowRuntime

		#region public static WorkflowInstance TryGetWorkflow(this WorkflowRuntime workflowRuntime, Guid instanceId)

		/// <summary>
		/// Tries to get the workflow with the given ID. If an exception occurs or
		/// if an instnace with the given ID is not found, null is returned.
		/// </summary>
		/// <param name="workflowRuntime">The current workflow runtime.</param>
		/// <param name="instanceId">The id of the workflow instnace to try to get.</param>
		public static WorkflowInstance TryGetWorkflow(this WorkflowRuntime workflowRuntime, Guid instanceId)
		{
			try
			{
				return workflowRuntime.GetWorkflow(instanceId);
			}
			catch { }

			return null;
		}

		#endregion
		
		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Workflow.ComponentModel.Activity

		#region public static T FindParentActivity<T>(this Activity activity)

		/// <summary>
		/// Returns the first parent or ancestor for the current activity
		/// that is of the given type.
		/// </summary>
		/// <typeparam name="T">The type of parent activity to find.</typeparam>
		/// <param name="activity">The current activity.</param>
		/// <returns>
		/// If no parent or ancestor is found, the default value of the type
		/// specified in <typeparamref name="T"/> is returned.
		/// </returns>
		public static T FindParentActivity<T>(this Activity activity)
		{
			if (null != activity.Parent)
			{
				if (activity.Parent is T)
				{
					return (T)(object)activity.Parent;
				}
				else
				{
					return activity.Parent.FindParentActivity<T>();
				}
			}

			return default(T);
		}

		#endregion
		
		#region public static ITargetMonitoringWorkflow GetMonitoringWorkflow(this Activity activity)

		/// <summary>
		/// Returns the monitoring workflow that hosts the current activity.
		/// </summary>
		/// <param name="activity">The current activity.</param>
		public static ITargetMonitoringWorkflow GetMonitoringWorkflow(this Activity activity)
		{

			Activity parent = activity;
			while (null != parent)
			{
				if (parent.GetType().IsTargetMonitoringWorkflow())
				{
					return (ITargetMonitoringWorkflow)parent;
				}
				parent = parent.Parent;
			}

			return null;
		}

		#endregion

		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Collections.Generic.IDictionary<string, object>

		#region public static bool EqualsDictionary(this IDictionary<string, object> dictionary, IDictionary<string, object> other)

		/// <summary>
		/// Compares the given dictionary to the current dictionary and returns
		/// true if the both dictionaries are equal.
		/// </summary>
		/// <param name="dictionary">The current dictionary.</param>
		/// <param name="other">The other dictionary to compare to the current.</param>
		public static bool EqualsDictionary(this IDictionary<string, object> dictionary, IDictionary<string, object> other)
		{
			if (null == other)
				return false;

			if (dictionary.Count != other.Count)
				return false;

			List<string> keysThis = new List<string>(dictionary.Keys);
			List<string> keysOther = new List<string>(other.Keys);

			for (int i = 0; i < keysThis.Count; i++)
			{
				if (!string.Equals(keysThis[i], keysOther[i]))
					return false;

				if (!object.Equals(dictionary[keysThis[i]], other[keysOther[i]]))
					return false;
			}

			return true;
		}

		#endregion
		
		//-------------------------------------------------------------------


		//-------------------------------------------------------------------
		// System.Object

		#region public static byte[] SerializeToBinary(this object o)

		/// <summary>
		/// Serializes the current object to a byte array.
		/// </summary>
		/// <param name="o">The current object.</param>
		public static byte[] SerializeToBinary(this object o)
		{
			if (!o.GetType().IsSerializable)
				throw new InvalidOperationException(string.Format("The type '{0}' is not serializable, which is required by the current operation.", o.GetType()));

			BinaryFormatter bf = new BinaryFormatter();
			using (MemoryStream strm = new MemoryStream())
			{
				bf.Serialize(strm, o);
				strm.Position = 0;

				byte[] buffer = new byte[strm.Length];
				strm.Read(buffer, 0, buffer.Length);

				return buffer;
			}
		}

		#endregion

		//-------------------------------------------------------------------

	}
}
